#ifndef CPUARGUMENTWIDGET_H
#define CPUARGUMENTWIDGET_H

#include <QWidget>
#include <vector>

#include "StdTable.h"
#include "StringUtil.h"

namespace Ui {
class CPUArgumentWidget;
}

class CPUArgumentWidget : public QWidget {
  Q_OBJECT

 public:
  explicit CPUArgumentWidget(QWidget* parent = 0);
  virtual ~CPUArgumentWidget();

  static QString defaultArgFormat(const QString& format,
                                  const QString& expression) {
    if (format.length()) return format;
    return QString("%1 {p:%1} {a:%1}").arg(expression).trimmed();
  }

  static QString defaultArgName(const QString& name, int argN) {
    if (name.length()) return name;
    return QString("%1").arg(argN);
  }

  static QString defaultArgFieldFormat(const QString& argName,
                                       const QString& argText) {
    if (argText.length()) return QString("%1: %2").arg(argName).arg(argText);
    return QString();
  }

 public slots:
  void disassembleAtSlot(dsint addr, dsint cip);
  void refreshData();

 private slots:
  void contextMenuSlot(QPoint pos);
  void followDisasmSlot();
  void followDumpSlot();
  void followStackSlot();
  void on_comboCallingConvention_currentIndexChanged(int index);
  void on_spinArgCount_valueChanged(int arg1);
  void on_checkBoxLock_stateChanged(int arg1);

 protected:
  struct Argument {
    QString name;
    QString expression32;
    QString expression64;
    QString format32;
    QString format64;

    const QString& getExpression() const {
      return ArchValue(expression32, expression64);
    }

    QString getFormat() const {
      return CPUArgumentWidget::defaultArgFormat(ArchValue(format32, format64),
                                                 getExpression());
    }

    explicit Argument(const QString& name, const QString& expression32,
                      const QString& expression64, const QString& format32 = "",
                      const QString& format64 = "")
        : name(name),
          expression32(expression32),
          expression64(expression64),
          format32(format32),
          format64(format64) {}
  };

  struct CallingConvention {
    QString name;
    int stackArgCount;
    QString stackLocation32;
    duint stackOffset32;
    duint callOffset32;
    QString stackLocation64;
    duint callOffset64;
    duint stackOffset64;
    std::vector<Argument> arguments;

    const QString& getStackLocation() const {
      return ArchValue(stackLocation32, stackLocation64);
    }

    const duint getStackOffset() const {
      return ArchValue(stackOffset32, stackOffset64);
    }

    const duint& getCallOffset() const {
      return ArchValue(callOffset32, callOffset64);
    }

    void addArgument(const Argument& argument) {
      arguments.push_back(argument);
    }

    explicit CallingConvention(const QString& name, int stackArgCount = 0,
                               const QString& stackLocation32 = "esp",
                               duint stackOffset32 = 0,
                               duint callOffset32 = sizeof(duint),
                               const QString& stackLocation64 = "rsp",
                               duint stackOffset64 = 0x20,
                               duint callOffset64 = sizeof(duint))
        : name(name),
          stackArgCount(stackArgCount),
          stackLocation32(stackLocation32),
          stackOffset32(stackOffset32),
          callOffset32(callOffset32),
          stackLocation64(stackLocation64),
          stackOffset64(stackOffset64),
          callOffset64(callOffset64) {}
  };

  Ui::CPUArgumentWidget* ui;
  StdTable* mTable;
  int mCurrentCallingConvention;
  duint mStackOffset;
  bool mAllowUpdate;
  std::vector<CallingConvention> mCallingConventions;
  std::vector<duint> mArgumentValues;
  QAction* mFollowDisasm;
  QAction* mFollowAddrDisasm;
  QAction* mFollowDump;
  QAction* mFollowAddrDump;
  QAction* mFollowStack;
  QAction* mFollowAddrStack;

  virtual void loadConfig() = 0;
  void setupTable();

  void updateStackOffset(bool iscall);
};

class CPUArgumentWidgetA64 : public CPUArgumentWidget {
  Q_OBJECT

 public:
  explicit CPUArgumentWidgetA64(QWidget* parent = 0) : CPUArgumentWidget(parent) {
    loadConfig();
  }
  ~CPUArgumentWidgetA64() override {}

 protected:
  virtual void loadConfig() override;
};

class CPUArgumentWidgetX64 : public CPUArgumentWidget {
  Q_OBJECT

 public:
  explicit CPUArgumentWidgetX64(QWidget* parent = 0) : CPUArgumentWidget(parent)  {
    loadConfig();
  }
  ~CPUArgumentWidgetX64() override {}

 protected:
  virtual void loadConfig() override;
};

class CPUArgumentWidgetA32 : public CPUArgumentWidget {
  Q_OBJECT

 public:
  explicit CPUArgumentWidgetA32(QWidget* parent = 0) : CPUArgumentWidget(parent)  {
    loadConfig();
  }
  ~CPUArgumentWidgetA32() override {}

 protected:
  virtual void loadConfig() override;
};

class CPUArgumentWidgetX86 : public CPUArgumentWidget {
  Q_OBJECT

 public:
  explicit CPUArgumentWidgetX86(QWidget* parent = 0) : CPUArgumentWidget(parent)  {
    loadConfig();
  }
  ~CPUArgumentWidgetX86() override {}

 protected:
  virtual void loadConfig() override;
};


#endif  // CPUARGUMENTWIDGET_H
